/*
    Author: Christopher Diggins
    License: MIT Licence 1.0
    Website: http://www.cdiggins.com
*/

#ifndef CVML_PEEPHOLE_HPP
#define CVML_PEEPHOLE_HPP

void Peephole(Instruction* first)
{
    Instruction* i = first;
    while (i != NULL) 
    {
        OpCodeEnum nextop = op_noop;
        if (i->next != NULL)
            nextop = i->next->opcode;
        
		switch (i->opcode)
		{
		case op_pop:
            switch (nextop)
            {
            case op_pop:
                {
                    Instruction* tmp = i->next;
                    int cnt = 1;
                    while (tmp != NULL && tmp->opcode == op_pop || tmp->opcode == op_noop) {
                        tmp->SetOpCode(op_noop);
						if (tmp->opcode == op_pop)
							++cnt;
						tmp = tmp->next;
                    }
                    switch (cnt) 
                    {
                    case 1: break;
                    case 2: i->SetOpCode(op_pop2); break;
                    case 3: i->SetOpCode(op_pop3); break;
                    case 4: i->SetOpCode(op_pop4); break;
                    default: i->SetOpCode(op_popN, cnt); break;                    
                    }
                }
                break;
            default: 
                break;
            }
            break;
        case op_dup:
            switch (nextop) 
            {
            case op_pop:
            case op_popd:
                i->SetNoop();
                i->next->SetNoop();
                break;
            case op_swap:
                i->next->SetNoop();
                break;
            default: 
                break;
            }
            break;
		case op_swapN:
			if (i->operand == 0)
				i->SetNoop();
			else if (i->operand == 1)
				i->SetOpCode(op_swap);
			break;
		case op_digN:
			if (i->operand == 0)
				i->SetNoop();
			else if (i->operand == 1)
				i->SetOpCode(op_swap);
			break;
		case op_popd:
            if (nextop == op_pop) {
                i->SetOpCode(op_pop);
            }
            break;
		case op_zapN:
			if (i->operand == 0)
				i->SetOpCode(op_pop);
			break;
        default:
            break;
        }
        if (i != NULL) 
            i = i->next;
    }
}

void Peephole(ByteCode* bc)
{
    for (int i=0; i < bc->GetNumSubs(); ++i) {
        Peephole(bc->GetSub(i)->first);     
    }
    bc->StripNoops();
}

#endif
